package com.tsyz.servlet.exception;

import com.tsyz.servlet.domain.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * 全局异常处理器
 * 统一捕获并处理控制器层抛出的所有异常，返回标准化的Result响应
 * 异常处理优先级：自定义异常 > 通用Exception > Throwable
 *
 * @author ext.zhaoke14
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 请求信息日志格式（URI:请求路径, Method:请求方法, IP:客户端IP）
     */
    private static final String REQUEST_INFO_FORMAT = "请求信息: URI=%s, Method=%s, IP=%s";

    /**
     * 提取请求基本信息（处理null安全）
     */
    private String getRequestInfo(HttpServletRequest request) {
        if (request == null) {
            return String.format(REQUEST_INFO_FORMAT, "空", "空", "空");
        }
        // 使用Objects.toString处理null，默认值为"空"
        String uri = Objects.toString(request.getRequestURI(), "空");
        String method = Objects.toString(request.getMethod(), "空");
        String ip = Objects.toString(request.getRemoteAddr(), "空");
        return String.format(REQUEST_INFO_FORMAT, uri, method, ip);
    }

    /**
     * 通用异常处理模板（减少重复代码）
     *
     * @param e         异常对象
     * @param request   请求对象
     * @param logPrefix 日志前缀（区分异常类型）
     * @param code      错误码
     * @param message   错误信息
     * @return 标准化响应结果
     */
    private <E extends Throwable> Result<?> handleException(E e, HttpServletRequest request,
                                                            String logPrefix, Integer code, String message) {
        String requestInfo = getRequestInfo(request);
        // 打印日志时包含异常堆栈（便于排查问题）
        log.error("{}: {}, 错误码={}, 错误信息={}", logPrefix, requestInfo, code, message, e);
        return Result.error(code, message);
    }

    /**
     * 处理参数校验异常（客户端参数错误）
     * 场景：请求参数格式错误、必填项缺失等
     */
    @ExceptionHandler(ParamException.class)
    @ResponseStatus(HttpStatus.OK) // 统一返回200，通过Result.code区分业务状态
    public Result<?> handleParamException(ParamException e, HttpServletRequest request) {
        return handleException(e, request, "参数校验异常", e.getCode(), e.getMessage());
    }

    /**
     * 处理业务逻辑异常（服务端业务错误）
     * 场景：订单状态异常、权限不足等预期内业务错误
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result<?> handleBusinessException(BusinessException e, HttpServletRequest request) {
        return handleException(e, request, "业务逻辑异常", e.getCode(), e.getMessage());
    }

    /**
     * 处理三方服务调用异常（外部依赖错误）
     * 场景：RPC调用失败、接口超时等
     */
    @ExceptionHandler(RpcException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result<?> handleRpcException(RpcException e, HttpServletRequest request) {
        return handleException(e, request, "三方服务调用异常", e.getCode(), e.getMessage());
    }

    /**
     * 处理未捕获的通用异常（程序运行时错误）
     * 场景：空指针、数组越界等未预期的代码错误
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.OK)
    public Result<?> handleException(Exception e, HttpServletRequest request) {
        String requestInfo = getRequestInfo(request);
        // 未预期异常需打印完整堆栈
        log.error("未捕获通用异常: {}", requestInfo, e);
        return Result.error(ErrorEnumCode.EXCEPTION_ERROR);
    }

    /**
     * 处理严重错误（JVM级错误）
     * 场景：内存溢出、类加载失败等致命错误
     */
    @ExceptionHandler(Throwable.class)
    @ResponseStatus(HttpStatus.OK)
    public Result<?> handleFatalError(Throwable t, HttpServletRequest request) {
        String requestInfo = getRequestInfo(request);
        log.error("严重系统错误: {}", requestInfo, t);
        return Result.error(ErrorEnumCode.THROWABLE_ERROR);
    }


    @ExceptionHandler(NoHandlerFoundException.class)
    public ResponseEntity<Result<String>> handleNoHandlerFound(NoHandlerFoundException e) {
        Result<String> result = Result.error("请求的资源不存在: " + e.getRequestURL());
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(result);
    }
}